#include "keyboard.h"
#include "memory.h"
#include "apic.h"
#include "interrupt.h"
#include "printk.h"

st_keyboard_in_buf *g_p_kb = NULL;
static int s_shift_l, s_shift_r, s_ctrl_l, s_ctrl_r, s_alt_l, s_alt_r;

st_hw_int_type s_keyboard_int_controller = {
    .enable = ioapic_enable,
    .disable = ioapic_disable,
    .install = ioapic_install,
    .uninstall = ioapic_uninstall,
    .ack = ioapic_edge_ack,
};

void keyboard_handler(unsigned long nr, unsigned long param, st_pt_regs *regs) {
    unsigned char x;

    x = io_in8(0x60);
    color_printk(WHITE, BLACK, "(K:%02x)", x);
    if (g_p_kb->p_head == g_p_kb->buf + KB_BUF_SIZE) {
        g_p_kb->p_head = g_p_kb->buf;
    }

    *g_p_kb->p_head = x;
    g_p_kb->count++;
    g_p_kb->p_head++;
}

void keyboard_init() {
    st_io_apic_rte_entry entry;
    unsigned long i, j;
    g_p_kb = (st_keyboard_in_buf *)kmalloc(sizeof(st_keyboard_in_buf), 0);
    g_p_kb->p_head = g_p_kb->buf;
    g_p_kb->p_tail = g_p_kb->buf;
    g_p_kb->count = 0;
    memset(g_p_kb->buf, 0, KB_BUF_SIZE);

    entry.vector = 0x21;        // 键盘中断向量号
    entry.deliver_mode = APIC_ICR_IOAPIC_FIXED;
    entry.dest_mode = ICR_IOAPIC_DELV_PHYSICAL;
    entry.deliver_status = APIC_ICR_IOAPIC_IDLE;
    entry.polarity = APIC_IOAPIC_POLARITY_HIGH;
    entry.irr = APIC_IOAPIC_IRR_RESET;
    entry.trigger = APIC_ICR_IOAPIC_EDGE;
    entry.mask = APIC_ICR_IOAPIC_MASKED;
    entry.reserved = 0;
    
    entry.destination.physical.reserved1 = 0;
    entry.destination.physical.phy_dest = 0;
    entry.destination.physical.reserved2 = 0;

    WAIT_KB_WRITE();
    io_out8(PORT_KB_CMD, KBCMD_WRITE_CMD);
    WAIT_KB_WRITE();
    io_out8(PORT_KB_DATA, KB_INIT_MODE);

    // 忙等待
    for (i = 0; i < 1000; ++i) {
        for (j = 0; j < 1000; ++j) {
            NOP();
        }
    }

    s_shift_l = 0;
    s_shift_r = 0;
    s_ctrl_l = 0;
    s_ctrl_r = 0;
    s_alt_l = 0;
    s_alt_r = 0;

    register_irq(entry.vector, &entry, &keyboard_handler, (unsigned long)g_p_kb, &s_keyboard_int_controller, "ps/2 keyboard");
}

void keyboard_exit() {
    unregister_irq(0x21);
    kfree(g_p_kb);
}

// 从键盘数据缓存队列拿出读取到的数据
unsigned char get_scancode() {
    unsigned char ret = 0;

    if (g_p_kb->count == 0) {
        while (!g_p_kb->count) {
            NOP();
        }
    }

    if (g_p_kb->p_tail == g_p_kb->buf + KB_BUF_SIZE) {
        g_p_kb->p_tail = g_p_kb->buf;
    }

    ret = *g_p_kb->p_tail;
    g_p_kb->count--;
    g_p_kb->p_tail++;
    return ret;
}

unsigned char g_pausebreak_scode[] = { 0xe1, 0x1d, 0x45, 0xe1, 0x9d, 0xc5 };
unsigned int g_keycode_map_normal[NR_SCAN_CODES * MAP_COLS] = {
    // unShift      Shift           scan-code
    0,              0,              // 0x00
    0,              0,              // 0x01     ESC
    '1',            '!',            // 0x02
    '2',            '@',            // 0x03
    '3',            '#',            // 0x04
    '4',            '$',            // 0x05
    '5',            '%',            // 0x06
    '6',            '^',            // 0x07
    '7',            '&',            // 0x08
    '8',            '*',            // 0x09
    '9',            '(',            // 0x0a
    '0',            ')',            // 0x0b
    '-',            '_',            // 0x0c
    '=',            '+',            // 0x0d
    0,              0,              // 0x0e     BACKSPACE
    0,              0,              // 0x0f     TAB
    'q',            'Q',            // 0x10
    'w',		    'W',            // 0x11
    'e',		    'E',            // 0x12
    'r',		    'R',            // 0x13
    't',		    'T',            // 0x14
    'y',		    'Y',            // 0x15
    'u',		    'U',            // 0x16
    'i',		    'I',            // 0x17
    'o',		    'O',            // 0x18
    'p',		    'P',            // 0x19
    '[',		    '{',            // 0x1a
    ']',		    '}',            // 0x1b
    0,		        0,		        // 0x1c     ENTER
    0x1d,		    0x1d,		    // 0x1d     CTRL Left
    'a',		    'A',            // 0x1e
    's',		    'S',            // 0x1f
    'd',		    'D',            // 0x20
    'f',		    'F',            // 0x21
    'g',		    'G',            // 0x22
    'h',		    'H',            // 0x23
    'j',		    'J',            // 0x24
    'k',		    'K',            // 0x25
    'l',		    'L',            // 0x26
    ';',		    ':',            // 0x27
    '\'',		    '"',            // 0x28
    '`',		    '~',            // 0x29
    0x2a,		    0x2a,		    // 0x2a     SHIFT Left
    '\\',		    '|',            // 0x2b
    'z',		    'Z',            // 0x2c
    'x',		    'X',            // 0x2d
    'c',		    'C',            // 0x2e
    'v',		    'V',            // 0x2f
    'b',		    'B',            // 0x30
    'n',		    'N',            // 0x31
    'm',		    'M',            // 0x32
    ',',		    '<',            // 0x33
    '.',		    '>',            // 0x34
    '/',		    '?',            // 0x35
    0x36,		    0x36,		    // 0x36     SHIFT Right
    '*',		    '*',            // 0x37
    0x38,		    0x38,		    // 0x38     ALT Left
    ' ',		    ' ',            // 0x39
    0,		        0,		        // 0x3a     CAPS LOCK
    0,		        0,		        // 0x3b     F1
    0,		        0,		        // 0x3c     F2
    0,		        0,		        // 0x3d     F3
    0,		        0,		        // 0x3e     F4
    0,		        0,		        // 0x3f     F5
    0,		        0,		        // 0x40     F6
    0,		        0,		        // 0x41     F7
    0,		        0,		        // 0x42     F8
    0,		        0,		        // 0x43     F9
    0,		        0,		        // 0x44     F10
    0,		        0,		        // 0x45     NUM LOCK
    0,		        0,		        // 0x46     SCROLL LOCK
    '7',		    0,		        // 0x47     PAD HONE
    '8',		    0,		        // 0x48     PAD UP
    '9',		    0,		        // 0x49     PAD PAGEUP
    '-',		    0,		        // 0x4a     PAD MINUS
    '4',		    0,		        // 0x4b     PAD LEFT
    '5',		    0,		        // 0x4c     PAD MID
    '6',		    0,		        // 0x4d     PAD RIGHT
    '+',		    0,		        // 0x4e     PAD PLUS
    '1',		    0,		        // 0x4f     PAD END
    '2',		    0,		        // 0x50     PAD DOWN
    '3',		    0,		        // 0x51     PAD PAGEDOWN
    '0',		    0,		        // 0x52     PAD INS
    '.',		    0,		        // 0x53     PAD DOT
    0,		        0,              // 0x54
    0,		        0,              // 0x55
    0,		        0,              // 0x56
    0,		        0,		        // 0x57     F11
    0,		        0,		        // 0x58     F12
    0,		        0,		        // 0x59
    0,		        0,              // 0x5a
    0,		        0,              // 0x5b
    0,		        0,              // 0x5c
    0,		        0,              // 0x5d
    0,		        0,              // 0x5e
    0,		        0,              // 0x5f
    0,		        0,              // 0x60
    0,		        0,              // 0x61
    0,		        0,              // 0x62
    0,		        0,              // 0x63
    0,		        0,              // 0x64
    0,		        0,              // 0x65
    0,		        0,              // 0x66
    0,		        0,              // 0x67
    0,		        0,              // 0x68
    0,		        0,              // 0x69
    0,		        0,              // 0x6a
    0,		        0,              // 0x6b
    0,		        0,              // 0x6c
    0,		        0,              // 0x6d
    0,		        0,              // 0x6e
    0,		        0,              // 0x6f
    0,		        0,              // 0x70
    0,		        0,              // 0x71
    0,		        0,              // 0x72
    0,		        0,              // 0x73
    0,		        0,              // 0x74
    0,		        0,              // 0x75
    0,		        0,              // 0x76
    0,		        0,              // 0x77
    0,		        0,              // 0x78
    0,		        0,              // 0x79
    0,		        0,              // 0x7a
    0,		        0,              // 0x7b
    0,		        0,              // 0x7c
    0,		        0,              // 0x7d
    0,		        0,              // 0x7e
    0,		        0,              // 0x7f
};

void analysis_keycode() {
    unsigned char x = 0;
    int i;
    int key = 0;
    int make = 0;           // 是否按下

    x = get_scancode();
    if (x == 0xe1) {        // pause break
        key = PAUSEBREAK;
        for (i = 1; i < 6; ++i) {
            if (get_scancode() != g_pausebreak_scode[i]) {
                key = 0;
                break;
            }
        }
    } else if (x == 0xe0) {     // print screen
        x = get_scancode();
        switch (x)
        {
        case 0x2a:              // 按下printscreen
            if (get_scancode() == 0xe0) {   
                if (get_scancode() == 0x37) {
                    key = PRINTSCREEN;
                    make = 1;
                }
            }
            break;
        case 0xb7:              // 弹起printscreen
            if (get_scancode() == 0xe0) {
                if (get_scancode() == 0xaa) {
                    key = PRINTSCREEN;
                    make = 0;
                }
            }
            break;
        case 0x1d:              // 按下右Ctrl
            s_ctrl_r = 1;
            key = OTHERKEY;
            break;
        case 0x9d:              // 弹起右Ctrl
            s_ctrl_r = 0;
            key = OTHERKEY;
            break;
        case 0x38:              // 按下右Alt
            s_alt_r = 1;
            key = OTHERKEY;
            break;
        case 0xb8:              // 弹起右Alt
            s_alt_r = 0;
            key = OTHERKEY;
            break;
        default:
            key = OTHERKEY;
            break;
        }
    }

    if (key == 0) {
        unsigned int *keyrow = NULL;
        int col = 0;

        make = (x & FLAG_BREAK ? 0 : 1);
        keyrow = &(g_keycode_map_normal[(x & 0x7f) * MAP_COLS]);

        if (s_shift_l || s_shift_r) {
            col = 1;
        }

        key = keyrow[col];
        switch (x & 0x7f) {       
            case 0x2a:          // 左Shift
                s_shift_l = make;
                key = 0;
                break;
            case 0x36:          // 右Shift
                s_shift_r = make;
                key = 0;
                break;
            case 0x1d:          // 左Ctrl
                s_ctrl_l = make;
                key = 0;
                break;
            case 0x38:          // 左Alt
                s_alt_l = make;
                key = 0;
                break;
            default:
                if (!make) {
                    key = 0;
                }
                break;
        }

        if (key) {
            color_printk(RED, BLACK, "(K:%c)\t", key);
        }
    }
}